package com.franklin.ideaplugin.easytesting.core.rpc;

import cn.hutool.core.util.IdUtil;
import com.franklin.ideaplugin.easytesting.common.cache.EasyTestingCache;
import com.franklin.ideaplugin.easytesting.common.constants.EasyTestingHeaders;
import com.franklin.ideaplugin.easytesting.common.constants.EasyTestingInnerHeaders;
import com.franklin.ideaplugin.easytesting.common.entity.ETRsp;
import com.franklin.ideaplugin.easytesting.common.entity.MethodInvokeData;
import com.franklin.ideaplugin.easytesting.common.exception.ETAssert;
import com.franklin.ideaplugin.easytesting.common.utils.JsonUtils;
import com.franklin.ideaplugin.easytesting.common.utils.MethodUtils;
import com.franklin.ideaplugin.easytesting.core.invoke.IMethodInvoker;
import com.franklin.ideaplugin.easytesting.core.invoke.MethodInvokerRegistry;
import com.franklin.ideaplugin.easytesting.common.log.ILogger;
import com.franklin.ideaplugin.easytesting.common.log.LoggerFactory;
import com.franklin.ideaplugin.easytesting.core.invoke.interceptor.GenericParamTypeMethodInvokeInterceptor;
import com.franklin.ideaplugin.easytesting.core.invoke.interceptor.IMethodInvokeInterceptor;
import com.franklin.ideaplugin.easytesting.core.invoke.interceptor.MethodInvokeInterceptorFactory;
import com.franklin.ideaplugin.easytesting.core.invoke.interceptor.ScriptMethodInvokeInterceptor;
import com.franklin.ideaplugin.easytesting.core.registry.FileRegistry;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;

/**
 * @author Ye Junhui
 * @since 2023/5/15
 */
public class MethodInvokeRequestHandler implements INettyHttpRequestHandler<MethodInvokeData, ETRsp<Object>> {

    private static final ILogger log = LoggerFactory.getLogger(MethodInvokeRequestHandler.class);

    @Override
    public String getRequestUrl() {
        return "/execute";
    }

    @Override
    public ETRsp<Object> handleRequest(MethodInvokeData methodInvokeData) throws Throwable {
        methodInvokeData.getHeaderMap().put(EasyTestingInnerHeaders.TRACE_ID, EasyTestingCache.getTraceId());

        Class<?> targetClass = Class.forName(methodInvokeData.getClassQualifiedName());
        Class<?>[] paramTypes = MethodUtils.resolveParamTypes(methodInvokeData);

        ETAssert.isTrue(
                paramTypes.length == methodInvokeData.getParameterMap().size(),
                "method mapping fail!"
        );

        //参数值
        Object[] params = methodInvokeData.getParameterMap().values().stream()
                .map(MethodUtils::parseValue)
                .toArray();
        ETAssert.isTrue(
                params.length == methodInvokeData.getParameterMap().size(),
                "method mapping fail!"
        );
        //方法
        Method declaredMethod = targetClass.getDeclaredMethod(methodInvokeData.getMethodName(), paramTypes);
        declaredMethod.setAccessible(true);
        Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();

        //拦截器
        MethodInvokeInterceptorFactory.getInstance().intercept(methodInvokeData,genericParameterTypes,params);

        List<IMethodInvoker> methodInvokerList = MethodInvokerRegistry.getMethodInvokerList();
        for (IMethodInvoker iMethodInvoker : methodInvokerList) {
            Object result = iMethodInvoker.invoke(methodInvokeData, targetClass, declaredMethod, paramTypes, params);
            ETRsp<Object> etRsp = null;
            if (result instanceof ETRsp) {
                etRsp = ((ETRsp<Object>) result);
            } else if (result instanceof Throwable) {
                Throwable throwable = (Throwable) result;
                etRsp = ETRsp.fail(throwable);
            } else if (result instanceof String) {
                String string = (String) result;
                log.info("Easy-Testing -> method execute result : \n{}", string);
                try {
                    LinkedHashMap linkedHashMap = JsonUtils.parseObject(string, LinkedHashMap.class);
                    if (Objects.nonNull(linkedHashMap)) {
                        etRsp = ETRsp.success(linkedHashMap);
                    } else {
                        etRsp = ETRsp.success(string);
                    }
                } catch (Exception e) {
                    etRsp = ETRsp.success(string);
                }
            } else {
                String resultString = JsonUtils.toJSONString(result);
                log.info("Easy-Testing -> method execute result : \n{}", resultString);
                etRsp = ETRsp.success(resultString);
            }
            ETRsp<Object> finalEtRsp = etRsp;
            FileRegistry.registryResult(methodInvokeData.getExecutePath(), finalEtRsp);
//            EasyTestingThreadPool.getBizThreadPool().execute(() -> {
//            });
            return etRsp;
        }
        return ETRsp.success(null);
    }

}
